/*
>> PixelToGlobalOp.s
>>
>> Pixel to global operation skeleton, allowing buffer and scalar inputs,
>> and buffer outputs.
>>
>> Author: Wouter Caarls
>> Date:   14-06-2006
*/

#include "ilpstrategies.h"

stratego code makeabsolute(code param, code body, int stride)
{
  strategies
    main = ?(param, body, stride); !(body, (param,stride));
      try(env-alltd(MakeAbsolute)); bottomup(try(apply-conc))

  rules
    MakeAbsolute:
      (ArrayIndex(ArrayIndex(p, i1), i2), (p, s)) ->
      c:exp |[ ~exp:p [ ~exp:i1 * ~exp:s + ~exp:i2 ] ]|

    MakeAbsolute:
      (p, (p, s)) -> p

    apply-conc:
      Conc(list1, list2) -> <conc>(list1, list2)
}

void PixelToGlobalOp(argcode_t *argcode, int arguments, code body)
{
  int _fi=-1, _ii, _stride, _line;
  int _hpixels;

  if (argcode[0].argtype != ARG_SCALAR_IN ||
      argcode[1].argtype != ARG_SCALAR_IN)
    error("First two arguments should be width and height");

  for (_ii=2; _ii < arguments; _ii++)
  {
    /* Allocate arguments */
    if (argcode[_ii].argtype == ARG_STREAM_IN)
    {
      /* Adjust first input */
      if (_fi == -1)
        _fi = _ii;
       
      @declare(addrpointer(argcode[_ii].datatype), argcode[_ii].id);
      body = replace(argcode[_ii].id, `&($@argcode[_ii].id)[_ii]`, body);
    }
    else if (argcode[_ii].argtype == ARG_STREAM_OUT)
    {
      @declare(addpointer(argcode[_ii].datatype), argcode[_ii].id);
      @argcode[_ii].id = @cast(addpointer(argcode[_ii].datatype),
 			    malloc((@argcode[0].argid).data.d*(@argcode[1].argid).data.d*
                            sizeof(argcode[_ii].datatype)));
      memset(@argcode[_ii].id, 0, (@argcode[0].argid).data.d*(@argcode[1].argid).data.d*
                               sizeof(argcode[_ii].datatype));

      body = makeabsolute(argcode[_ii].id, body, (@argcode[0].argid).data.d);
    }
    else
    {   
      /* Non-streams must be initialised */
      @declare(addrpointer(argcode[_ii].datatype), argcode[_ii].id);
      if (@dataclass(argcode[_ii].datatype) == DATACLASS_INTEGER)   
        @argcode[_ii].id = &(@argcode[_ii].argid).data.d;
      else if (@dataclass(argcode[_ii].datatype) == DATACLASS_FLOAT)
        @argcode[_ii].id = &(@argcode[_ii].argid).data.f;
       else
        @argcode[_ii].id = cast(addpointer(argcode[_ii].datatype), (@argcode[_ii].argid).data.b);
    }
  }  

  _stride = scuBufferGetStride((@argcode[_fi].argid).buffer);

  while (_hpixels = scuBufferPeek((@argcode[_fi].argid).buffer, (@argcode[_fi].argid).reader,
                      (void**)&(@argcode[_fi].id), _stride*sizeof(argcode[_fi].datatype), SCU_BUF_BLOCK_ALL))
  {
    int _ii;

    _hpixels = _hpixels / sizeof(argcode[_fi].datatype);

    /* Peek all inputs */
    for (_ii=0; _ii < arguments; _ii++)
      if (_ii != _fi && argcode[_ii].argtype == ARG_STREAM_IN)
        scuBufferPeek((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader,
                      (void**)&(@argcode[_ii].id), _hpixels*sizeof(argcode[_ii].datatype), SCU_BUF_BLOCK_ALL);

    for (_ii=0; _ii < _hpixels; _ii++)
      @body;
     
    /* Release stuff */
    for (_ii=0; _ii < arguments; _ii++)
      if (argcode[_ii].argtype == ARG_STREAM_IN)
        scuBufferReleasePeeked((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader,
                               _hpixels*sizeof(argcode[_ii].datatype));
  }  
  
  /* Write outputs */
  for (_line=0; _line < (@argcode[1].argid).data.d; _line++)
  {
    for (_ii=0; _ii < arguments; _ii++)
      if (argcode[_ii].argtype == ARG_STREAM_OUT)
        scuBufferWrite((@argcode[_ii].argid).buffer,
                       &((@argcode[_ii].id)[_line*(@argcode[0].argid).data.d*sizeof(argcode[_ii].datatype)]),
                       (@argcode[0].argid).data.d*sizeof(argcode[_ii].datatype),
                       SCU_BUF_BLOCK_ALL);
  }

  finalizearguments_direct(argcode, arguments);

  /* Set properties */
  propEnvReset();
  propEnv("operation", operation);
 
  for (_ii=0; _ii < arguments; _ii++)
  {
    propEnv("argument", @idtostring(makeid(`id`, _ii)));
      propSeti("id", _ii);
      propEnv("target", target);

      propSeti("buffer", 1);
      propSeti("state", 0);
      propSeti("delay", 0);

      propSeti("elementsize", sizeof(argcode[_ii].datatype));

      if (argcode[_ii].argtype == ARG_STREAM_IN || argcode[_ii].argtype == ARG_STREAM_OUT)
        propSeti("chunksize", 1024);
      else
        propSeti("chunksize", 1);

      if (argcode[_ii].argtype == ARG_STREAM_OUT)
        propSets("timedist", "bulk");
      else
        propSets("timedist", "regular");

      propEnvBack();
    propEnvBack();
  }
}
